﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mogre;
using MogreNewt;

namespace WastedMechanics
{
    public class Raycaster
    {
        public struct RaycastHit
        {
            public Body HitBody;
            public float HitDistance;
            public Vector3 HitNormal;
        }

        public class OnlyStatic : Raycast
        {
            public RaycastHit ClosestHit;

            public OnlyStatic()
            {
                ClosestHit = new RaycastHit();

                ClosestHit.HitDistance = -1.0f;    
            }

            public override bool UserPreFilterCallback(Body body)
            {
                return !(body.UserData is Character || (body.UserData is GameObject && (body.UserData as GameObject).IsObstacle == false));
            }

            public override bool UserCallback(Body body, float distance, Vector3 normal, int collisionID)
            {
                if (ClosestHit.HitDistance < 0 || distance < ClosestHit.HitDistance)
                {                    
                    ClosestHit.HitDistance = distance;
                    ClosestHit.HitBody = body;
                    ClosestHit.HitNormal = normal;
                    return true;
                }
                return false;
            }
        }

        public class PredicateRaycast : Raycast
        {
            public class ContactInfo
            {
                public float Distance;  
                public Body Body;
                public Vector3 Normal;
            }

            public Predicate<Body> Predicate;
            public List<ContactInfo> Contacts;

            public PredicateRaycast(Predicate<Body> pred)
            {
                Predicate = pred;
                Contacts = new List<ContactInfo>();
            }

            public override bool UserPreFilterCallback(Body body)
            {
                return Predicate(body);
            }

            public void SortContacts()
            {
                Contacts.Sort((a, b) => a.Distance.CompareTo(b.Distance));
            }

            public override bool UserCallback(Body body, float distance, Vector3 normal, int collisionID)
            {
                ContactInfo contact = new ContactInfo();
                contact.Distance = distance;
                contact.Body = body;
                contact.Normal = normal;
                Contacts.Add(contact);
                return true;
            }
        }



        public class ObjectContactRaycast : Raycast
        {
            public SelectableObject Contact;            
            float LastContactDistance;
            public SelectableObject Sender;
            public bool IgnoreDead;

            public ObjectContactRaycast(SelectableObject sender, bool ignoreDead = false)
            {
                Sender = sender;
                LastContactDistance = 1.0f;
                IgnoreDead = ignoreDead;                   
            }

            public override bool UserCallback(Body body, float distance, Vector3 normal, int collisionID)
            {                
                if (body.UserData == Sender || (body.UserData is GameObject && (body.UserData as GameObject).IsObstacle == false))     
                    return false;

                if (IgnoreDead && body.UserData is Character && (body.UserData as Character).IsDead)
                    return false;

                if (body.UserData is SelectableObject && distance < LastContactDistance)
                    Contact = body.UserData as SelectableObject;           

                if (distance < LastContactDistance)
                    LastContactDistance = distance;
                return false;
            }                       
        }

        public class ClimbEdgeRaycast : Raycast
        {
            public ClimbEdge ClimbEdge;

            public override bool UserCallback(Body body, float distance, Vector3 normal, int collisionID)
            {
                if (body.UserData is ClimbEdge)
                    ClimbEdge = body.UserData as ClimbEdge;
                return true;
            }
        }
    }
}
